﻿using Edutainment.Interfaces;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Speech.Synthesis;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Edutainment
{
    /// <summary>
    /// Interaktionslogik für BubbleSortExamWindow.xaml
    /// </summary>
    public partial class BubbleSortExamWindow : UserControl, ISortationInterface
    {
        private SpeechSynthesizer sSynthesizer;

        public ArrayList buttonList { get; set; }
        ArrayList clickedButtons;
        ArrayList wrongClickedButtons;

        ButtonController bController;
        SortationController sController;
        BubbleSort current;

        double _points;
        double points
        {
            get { return _points; }
            set 
            {
                _points = value;
                if (_points > 100)
                    _points = 100;
                updatePointsLabel();
            }
        }
        double availablePoints;
        double pointsPerStep;

        public BubbleSortExamWindow()
        {
            InitializeComponent();

            

            /*sSynthesizer = new SpeechSynthesizer();

            sSynthesizer.SetOutputToDefaultAudioDevice();
            sSynthesizer.Rate = -1;
            sSynthesizer.Volume = 100;
            sSynthesizer.SelectVoice("Microsoft Zira Desktop");*/

            App currentApp = (App)Application.Current;

            bController = currentApp.getAppManager().buttonController;
            sController = currentApp.getAppManager().sortController;

            clickedButtons = new ArrayList();
            wrongClickedButtons = new ArrayList();
            current = sController.bubbleSort;
        }

        public void setDelegate()
        {
            if (current != null)
            {
                current.sortationDelegate = this;
            }
        }


        private void initNewButtons()
        {
            if (current != null)
                current.sortationDelegate = null;
            sController.prepareBubbleSort(8, true);
            current = (BubbleSort)sController.currentSortAlgorithm;
            current.restart();
            bController.createButtonsWithList(sController.currentSortAlgorithm.getListToSort());

            buttonList = bController.buttons;
            current.sortationDelegate = this;

            points = 0;
            availablePoints = current.countOfSteps;
            pointsPerStep = 100.0 / availablePoints;

            PointsLabel.Opacity = 0.0;
        }


        private void removeButtons()
        {
            clickedButtons.Clear();
            wrongClickedButtons.Clear();

            if (buttonList != null)
                buttonList.Clear();
            ButtonPositionCanvas.Children.Clear();
        }

        public void showButtons(bool animated)
        {
            removeButtons();
            initNewButtons();

            if (buttonList != null && buttonList.Count > 0)
            {
                int buttonWidth = 80;
                int buttonMargin = 10;
                Canvas toUse = ButtonPositionCanvas;
                toUse.VerticalAlignment = VerticalAlignment.Center;
                double gridWidth = toUse.Width;

                int bCounter = 1;
                DoubleAnimationUsingKeyFrames animate = null;


                foreach (ToggleButton button in buttonList)
                {
                    Thickness bThickness = new Thickness((bCounter * buttonMargin) + ((bCounter - 1) * buttonWidth), 20, bCounter * buttonMargin, 0);
                    button.Margin = bThickness;
                    button.Width = buttonWidth;
                    button.Height = 100;
                    button.Visibility = Visibility.Visible;
                    button.Style = (Style)FindResource("InformButton");
                    button.Click += onToggleButtonClicked;
                    toUse.Children.Add(button);


                    if (animated)
                    {
                        button.Opacity = 0.0;
                        double kfDuration = (double)bCounter / 2.0;
                        double test = ((double)kfDuration) - .5;

                        animate = new DoubleAnimationUsingKeyFrames();
                        animate.Duration = TimeSpan.FromSeconds(kfDuration);
                        LinearDoubleKeyFrame dkf1 = new LinearDoubleKeyFrame(0.0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(test)));
                        LinearDoubleKeyFrame dkf2 = new LinearDoubleKeyFrame(1.0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(kfDuration)));

                        animate.KeyFrames.Add(dkf1);
                        animate.KeyFrames.Add(dkf2);

                        button.BeginAnimation(ToggleButton.OpacityProperty, animate);
                    }
                    bCounter++;

                }
            }
            current.start();
        }

        private void swapAnimationCompleted(object sender, EventArgs e)
        {
            if (clickedButtons.Count > 0)
            {
                ToggleButton button1 = clickedButtons[0] as ToggleButton;
                ToggleButton button2 = clickedButtons[1] as ToggleButton;

                button2.IsChecked = false;
                button1.IsChecked = false;

                clickedButtons.Clear();
            }
        }

        private void animateButtonSwap(int a, int b)
        {
            if (a >= 0 && a < buttonList.Count && b >= 0 && b < buttonList.Count)
            {
                ToggleButton buttonA = (ToggleButton)buttonList[a];
                ToggleButton buttonB = (ToggleButton)buttonList[b];

                Thickness thickA = buttonA.Margin;
                Thickness thickB = buttonB.Margin;

                Duration dur = new Duration(TimeSpan.FromSeconds(0.5));

                PowerEase pEase = new PowerEase();
                pEase.EasingMode = EasingMode.EaseInOut;

                ThicknessAnimation thickAnim1 = new ThicknessAnimation(thickB, dur);
                ThicknessAnimation thickAnim2 = new ThicknessAnimation(thickA, dur);
                thickAnim1.EasingFunction = pEase;
                thickAnim2.EasingFunction = pEase;

                Storyboard.SetTarget(thickAnim1, buttonA);
                Storyboard.SetTarget(thickAnim2, buttonB);
                Storyboard.SetTargetProperty(thickAnim1, new PropertyPath(ToggleButton.MarginProperty));
                Storyboard.SetTargetProperty(thickAnim2, new PropertyPath(ToggleButton.MarginProperty));

                Storyboard sbThickness = new Storyboard();
                sbThickness.Children.Add(thickAnim1);
                sbThickness.Children.Add(thickAnim2);

                sbThickness.Completed += swapAnimationCompleted;
                sbThickness.Begin();


            }

        }

        public void onToggleButtonClicked(object sender, RoutedEventArgs e)
        {
            ToggleButton pressedButton = sender as ToggleButton;

            /*if (wrongClickedButtons.Contains(pressedButton))
            {
                this.animateButtonSelection((ToggleButton)wrongClickedButtons[0], (ToggleButton)wrongClickedButtons[1], true);
                wrongClickedButtons.Clear();
            }*/


            if (clickedButtons.Contains(pressedButton))
            {
                clickedButtons.Remove(pressedButton);
            }
            else
            {
                if (clickedButtons.Count < 2)
                {
                    clickedButtons.Add(pressedButton);
                    if (clickedButtons.Count == 2)
                    {
                        int indxA = buttonList.IndexOf(clickedButtons[1]);
                        int indxB = buttonList.IndexOf(clickedButtons[0]);
                        if (indxA < indxB - 1 || indxA > indxB + 1)
                        {
                            //this.animateButtonSelection((ToggleButton)clickedButtons[1], (ToggleButton)clickedButtons[0], false);
                            //this.animateIncorrectSwap(indxA, indxB);
                            endSorting();
                        }
                        else
                        {
                            bool correct = current.correctStep(indxA, indxB);
                            if (correct)
                            {
                                current.swap(buttonList, indxA, indxB);
                                this.animateButtonSwap(indxA, indxB);
                                points += pointsPerStep;
                            }
                            else
                            {
                                endSorting();
                            }
                        }
                    }
                }
                else
                {
                    pressedButton.IsChecked = !pressedButton.IsChecked;
                }
            }
        }

        private void updatePointsLabel()
        {
            String s = String.Format("Punkte {0:0}/100", points);
            PointsLabel.Content = s;
        }

        private void animateVisibility(UIElement obj, double from, double to)
        {

            if (obj != null && obj.Opacity != to)
            {

                DoubleAnimation dAnim = new DoubleAnimation();
                dAnim.From = from;
                dAnim.To = to;
                dAnim.Duration = new Duration(TimeSpan.FromSeconds(2));

                Storyboard.SetTarget(dAnim, obj);
                Storyboard.SetTargetProperty(dAnim, new PropertyPath(UIElement.OpacityProperty));

                Storyboard sb = new Storyboard();
                sb.Children.Add(dAnim);
                sb.Begin();
            }
        }

        private void animateVisibility(UIElement obj, bool visible)
        {
            double from, to;
            if (visible)
            {
                from = 0.0;
                to = 1.0;
            }
            else
            {
                from = 1.0;
                to = 0.0;
            }

            animateVisibility(obj, from, to);
        }

        private void animateElementPosition(Control obj, Thickness from, Thickness to)
        {
            if (obj != null)
            {
                Thickness current = obj.Margin;
                if (!current.Equals(to))
                {
                    PowerEase pEase = new PowerEase();
                    pEase.EasingMode = EasingMode.EaseInOut;

                    ThicknessAnimation thickAnim = new ThicknessAnimation();
                    thickAnim.From = from;
                    thickAnim.To = to;
                    thickAnim.EasingFunction = pEase;

                    Storyboard.SetTarget(thickAnim, obj);
                    Storyboard.SetTargetProperty(thickAnim, new PropertyPath(Control.MarginProperty));

                    Storyboard sb = new Storyboard();
                    sb.Children.Add(thickAnim);
                    sb.Begin();
                }
            }
        }

        private void disableButtons()
        {
            foreach (ToggleButton btn in buttonList)
            {
                btn.IsEnabled = false;
            }
        }

        private void endSorting()
        {
            disableButtons();
            double percentage = points;
            String s = infoStringForPercentage(percentage);
            InfoLabel.Content = s;
            SizeLabelFont(InfoLabel);
            animateVisibility(PointsLabel, 0.0, 0.8);

            Thickness thickFrom = (Thickness)FindResource("ThicknessOut");
            Thickness thickTo = (Thickness)FindResource("ThicknessIn");
            InfoLabel.Opacity = 1.0;
            animateElementPosition(InfoLabel, thickFrom, thickTo);
        }

        private String infoStringForPercentage(double points)
        {
            String infoString;

            double perc = points / 100.0;

            if (perc == 0.0)
            {
                infoString = "Leider keine Punkte! Du solltest noch fleißig weiterüben.";
            }
            else if (perc > 0.0 && perc <= 0.3)
            {
                infoString = "Es sind noch größere Fehler zu erkennen. Übe noch weiter.";
            }
            else if (perc > 0.3 && perc <= 0.5)
            {
                infoString = "Das war schon gar nicht schlecht! Versuche durch üben deine Fehler zu verringern.";
            }
            else if (perc > 0.5 && perc <= 0.7)
            {
                infoString = "Du wirst immer besser! Es fehlt nicht mehr viel, bis du fehlerfrei bist.";
            }
            else if (perc > 0.7 && perc <= 0.99)
            {
                infoString = "Sehr gut! Nur winzige Fehler haben sich eingeschlichen.";
            }
            else
            {
                infoString = "Hervorragend! Du hast die Prüfung ohne Fehler bestanden. Jetzt bist du bereit für den nächsten Sortier-Algorithmus";
            }

            return infoString;
        }

        private void animateRestart(Object sender, RoutedEventArgs e)
        {
            animateVisibility(PointsLabel, 0.8, 0.0);
            //animateVisibility(InfoLabel, false);
            animateElementPosition(InfoLabel, (Thickness)FindResource("ThicknessIn"), (Thickness)FindResource("ThicknessOut"));
            restartSortation();
        }

        public void restartSortation()
        {
            current.restart();
            showButtons(true);
        }

        //InterfaceMethods

        public void hasCompletedSorting()
        {
            disableButtons();
            animateVisibility(PointsLabel, 0.0, 0.8);
            points = 100;
            endSorting();
        }

        public void buttonAtCorrectPosition(int index, double animDelay)
        {

        }

        private void SizeLabelFont(Label lbl)
        {
            // Only bother if there's text.
            string txt = (String)lbl.Content;
            if (txt.Length > 0)
            {
                // See how much room we have, allowing a bit
                // for the Label's internal margin.

                double wid = lbl.Width;
                double hgt = lbl.Height;

                FormattedText ft = new FormattedText((string)lbl.Content, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(lbl.FontFamily.ToString()), lbl.FontSize, null);
                double fSize = lbl.FontSize;

                if (ft != null)
                {
                    while (fSize > 0)
                    {
                        double width = ft.Width;
                        double height = ft.Height;
                        
                        if (width < wid && height < hgt)
                        {
                            break;
                        }
                        fSize--;
                        ft.SetFontSize(fSize);
                    }
                }

                lbl.FontSize = fSize;
            }
        }
    }
}
